from fastapi import APIRouter, Form, Request, Depends, HTTPException, Cookie
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session
from database import get_db
from models import Area, Passage, Device
from pydantic import BaseModel, validator
from typing import Optional
import paho.mqtt.client as mqtt
import json
import time
import threading
from datetime import datetime
from core.logger import app_logger as logger
from urllib.parse import unquote
from fastapi.responses import RedirectResponse
from sqlalchemy import or_
from utils import validate_session

router = APIRouter()
templates = Jinja2Templates(directory="templates")
templates.env.filters["urldecode"] = lambda x: unquote(x)    

# MQTT配置
MQTT_BROKER = "192.168.88.105"
MQTT_PORT = 1883
MQTT_USERNAME = "001"
MQTT_PASSWORD = "kaikai66"
CONTROL_TOPIC = "/edge_app_controller"
RESPONSE_TOPIC = "/edge_app_controller_reply"

ADD_EVENT = "/alg_media_config"
DELETE_EVENT = "/alg_media_delete"

# 全局变量存储MQTT响应
mqtt_responses = {}
response_lock = threading.Lock()

class PassageForm(BaseModel):
    MediaName: str
    MediaDesc: str
    MediaIp: str  # 新增：NVR IP地址
    MediaPort: int  # 新增：NVR端口
    MediaUser: str  # 新增：NVR用户名
    MediaPassword: str  # 新增：NVR密码
    MediaNum: str  # 新增：通道号
    BoardId: str
    AreaId: int
    passage_id: Optional[int] = None
    
    class Config:
        extra = "ignore"

class AreaForm(BaseModel):
    AreaName: str
    Latitude: Optional[float] = None
    Longitude: Optional[float] = None

# MQTT客户端初始化和连接
def init_mqtt_client():
    client = mqtt.Client()
    client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
    def on_connect(client, userdata, flags, rc):
        if rc == 0:
            print("MQTT客户端成功连接")
            client.subscribe(RESPONSE_TOPIC)
        else:
            print(f"MQTT连接失败，错误码: {rc}")
    
    def on_message(client, userdata, msg):
        try:
            payload = msg.payload.decode('utf-8')
            data = json.loads(payload)
            
            # 处理所有类型的事件响应（添加和删除）
            if data.get("Event") in [ADD_EVENT, DELETE_EVENT]:
                board_id = data.get("BoardId", "")
                
                # 标准化 BoardId 格式（确保与数据库格式一致）
                normalized_board_id = board_id.strip().upper()
                
                with response_lock:
                    mqtt_responses[normalized_board_id] = data
                    logger.info(f"收到MQTT响应: {data}")
        except Exception as e:
            print(f"MQTT消息处理错误: {str(e)}")
    
    client.on_connect = on_connect
    client.on_message = on_message
    
    try:
        client.connect(MQTT_BROKER, MQTT_PORT, 60)
        client.loop_start()
        return client
    except Exception as e:
        print(f"MQTT连接异常: {str(e)}")
        return None

# 全局MQTT客户端
mqtt_client = init_mqtt_client()

def send_mqtt_command(event: str, media_name: int, media_url: str, board_id: str):
    """发送MQTT命令并等待响应"""
    if not mqtt_client:
        return {"success": False, "message": "MQTT客户端未初始化"}
    
    # 标准化 BoardId
    normalized_board_id = board_id.strip().upper()
    
    # 清空该设备之前的响应
    with response_lock:
        if normalized_board_id in mqtt_responses:
            del mqtt_responses[normalized_board_id]
    
    # 发送命令
    if event == ADD_EVENT:
        logger.info(f"MQTT：发送新增/修改信息 {media_name} 到设备 {board_id}")
        payload = {
            "Event": event,
            "MediaName": str(media_name),
            "MediaUrl": media_url,
            "BoardId": board_id
        }
    elif event == DELETE_EVENT:
        logger.info(f"MQTT：发送删除信息 {media_name} 到设备 {board_id}")
        # 删除命令不需要MediaUrl参数
        payload = {
            "Event": event,
            "MediaName": media_name,
            "BoardId": board_id
        }

    message = json.dumps(payload)
    result = mqtt_client.publish(CONTROL_TOPIC, message)
    
    if result.rc != mqtt.MQTT_ERR_SUCCESS:
        return {"success": False, "message": f"MQTT发送失败，错误码: {result.rc}"}
    
    # 等待响应（最多10秒）
    timeout = 10
    start_time = time.time()
    logger.debug(f"开始等待设备响应: {normalized_board_id}")
    
    while time.time() - start_time < timeout:
        elapsed = time.time() - start_time
        logger.debug(f"等待设备 {normalized_board_id} 的响应... ({elapsed:.1f}/10 秒)")
        
        with response_lock:
            current_keys = list(mqtt_responses.keys())
            logger.debug(f"当前存储的响应键: {current_keys}")
            
            if normalized_board_id in mqtt_responses:
                response = mqtt_responses[normalized_board_id]
                del mqtt_responses[normalized_board_id]
                
                logger.info(f"收到设备响应: {response}")
                
                result_info = response.get("Result", {})
                if result_info.get("Code") == 0:
                    return {"success": True, "message": result_info.get("Desc", "操作成功")}
                else:
                    return {
                        "success": False,
                        "message": f"设备返回错误: {result_info.get('Code')} - {result_info.get('Desc', '未知错误')}"
                    }
        time.sleep(0.5)
    
    logger.warning(f"等待设备 {normalized_board_id} 响应超时")
    return {"success": False, "message": "等待设备响应超时"}

def save_passage_to_db(db: Session, form_data: PassageForm, device, is_edit=False, passage_id=None):
    """保存通道信息到数据库"""
    # 生成RTSP URL
    media_url = f"rtsp://{form_data.MediaUser}:{form_data.MediaPassword}@{form_data.MediaIp}:{form_data.MediaPort}/Streaming/Channels/{form_data.MediaNum}01"
    
    if is_edit:
        # 编辑现有通道
        passage = db.query(Passage).filter(
            Passage.PassageId == passage_id,
            Passage.DeadTime == None
        ).first()
        
        if not passage:
            raise HTTPException(status_code=404, detail="通道未找到")
        
        # 检查通道名称是否被修改（不允许修改名称）
        if form_data.MediaName != passage.MediaName:
            raise HTTPException(status_code=400, detail="通道名称不可修改")
        
        # 更新通道信息
        passage.MediaDesc = form_data.MediaDesc
        passage.MediaIp = form_data.MediaIp
        passage.MediaPort = form_data.MediaPort
        passage.MediaUser = form_data.MediaUser
        passage.MediaPassword = form_data.MediaPassword
        passage.MediaNum = form_data.MediaNum
        passage.BoardId = form_data.BoardId
        passage.BoardIp = device.BoardIp
        passage.AreaId = form_data.AreaId
        passage.UpdateTime = datetime.now()
        
        return passage, media_url
    else:
        # 添加新通道
        # 检查通道名称是否已存在
        existing_passage = db.query(Passage).filter(
            Passage.MediaName == form_data.MediaName,
            Passage.DeadTime == None
        ).first()
        
        if existing_passage:
            logger.warning(f"通道名称已存在: {form_data.MediaName}")
            raise HTTPException(status_code=400, detail="通道名称已存在")
        
        # 创建新通道
        new_passage = Passage(
            MediaName=form_data.MediaName,
            MediaDesc=form_data.MediaDesc,
            MediaIp=form_data.MediaIp,
            MediaPort=form_data.MediaPort,
            MediaUser=form_data.MediaUser,
            MediaPassword=form_data.MediaPassword,
            MediaNum=form_data.MediaNum,
            BoardId=form_data.BoardId,
            BoardIp=device.BoardIp,
            AreaId=form_data.AreaId,
            MediaStatus="正常"
        )
        
        db.add(new_passage)
        db.flush()  # 获取新通道ID但不提交事务
        
        return new_passage, media_url

@router.get("/video-channels")
@router.get("/video-channels/")
async def list_video_channels(request: Request,
                            SessionId: Optional[str] = Cookie(None),
                            UserName: Optional[str] = Cookie(None),
                               db: Session = Depends(get_db)):
    if not SessionId or not UserName or not validate_session(SessionId, UserName):
        return RedirectResponse(url="/")
    """展示所有视频通道"""
    logger.info("list_video_channels 方法被调用")
    
    try:
        # 获取搜索参数
        search_query = request.query_params.get('search', '').strip()

        # 基础查询（关联区域）
        query = db.query(
            Passage.PassageId,
            Passage.MediaName,
            Passage.MediaDesc,
            Passage.MediaIp,
            Passage.MediaPort,
            Passage.MediaUser,
            Passage.MediaPassword,
            Passage.MediaNum,
            Passage.BoardId,
            Passage.BoardIp,
            Passage.MediaStatus,
            Passage.AreaId,
            Area.AreaName
        ).outerjoin(Area, Passage.AreaId == Area.AreaId).filter(Passage.DeadTime == None)

        # 如果有搜索查询，添加过滤条件
        if search_query:
            query = query.filter(
                or_(
                    Passage.MediaName.ilike(f"%{search_query}%"),
                    Passage.MediaDesc.ilike(f"%{search_query}%"),
                    Passage.BoardIp.ilike(f"%{search_query}%")
                )
            )
        
        passages = query.all()
        
        # 查询设备
        logger.info("查询设备表...")
        devices = db.query(Device.BoardId, Device.BoardIp).all()
        logger.info(f"查询到 {len(devices)} 台设备")
        
        # 查询所有区域
        areas = db.query(Area).all()
        
        # 获取查询参数中的消息
        error_msg = request.query_params.get('error')
        success_msg = request.query_params.get('success')
        
        return templates.TemplateResponse(
            "video_channels.html",
            {
                "request": request,
                "passages": passages,
                "devices": devices,
                "areas": areas, 
                "current_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "error": error_msg,
                "success": success_msg,
                "search": search_query
            }
        )
    except Exception as e:
        logger.error(f"获取视频通道列表时出错: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail="获取数据失败")

@router.post("/video-channels/add")
async def add_video_channel(
    request: Request,
    form_data: PassageForm,
    db: Session = Depends(get_db)
):
    try:
        logger.debug(f"Received form data: {form_data.dict()}")
        
        # 获取设备IP地址
        device = db.query(Device).filter(Device.BoardId == form_data.BoardId).first()
        if not device:
            logger.warning(f"设备未找到: {form_data.BoardId}")
            raise HTTPException(status_code=400, detail="设备未找到")
        
        # 检查通道名称是否已存在
        existing_passage = db.query(Passage).filter(
            Passage.MediaName == form_data.MediaName,
            Passage.DeadTime == None
        ).first()
        
        if existing_passage:
            logger.warning(f"通道名称已存在: {form_data.MediaName}")
            raise HTTPException(status_code=400, detail="通道名称已存在")
        
        # 生成RTSP URL
        media_url = f"rtsp://{form_data.MediaUser}:{form_data.MediaPassword}@{form_data.MediaIp}:{form_data.MediaPort}/Streaming/Channels/{form_data.MediaNum}01"
        
        # 保存到数据库  
        new_passage = Passage(
            MediaName=form_data.MediaName,
            MediaDesc=form_data.MediaDesc,
            MediaIp=form_data.MediaIp,
            MediaPort=form_data.MediaPort,
            MediaUser=form_data.MediaUser,
            MediaPassword=form_data.MediaPassword,
            MediaNum=form_data.MediaNum,
            BoardId=form_data.BoardId,
            BoardIp=device.BoardIp,  # 确保这里存储设备IP
            AreaId=form_data.AreaId,
            MediaStatus="正常"
        )
        
        db.add(new_passage)
        db.flush()  # 获取新通道ID但不提交事务

        # 发送MQTT命令到设备
        mqtt_result = send_mqtt_command(
            ADD_EVENT,
            new_passage.PassageId,
            media_url,
            form_data.BoardId
        )
        
        if not mqtt_result["success"]:
            logger.error(f"设备配置失败: {mqtt_result['message']}")
            # 抛出异常让FastAPI自动回滚
            raise HTTPException(status_code=400, detail=f"设备配置失败: {mqtt_result['message']}")
        
        # 提交事务（FastAPI会自动提交）
        db.commit()
        logger.info(f"通道添加成功: {form_data.MediaName}")
        
        return {"success": True, "message": "通道添加成功"}
    
    except Exception as e:
        logger.exception(f"添加通道时发生错误: {str(e)}")
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(status_code=500, detail="服务器内部错误")
    
@router.post("/video-channels/{passage_id}/edit-area")
async def edit_passage_area(
    passage_id: int,
    area_id: int = Form(...),
    db: Session = Depends(get_db)
):
    """编辑通道的区域"""
    passage = db.query(Passage).filter(
        Passage.PassageId == passage_id,
        Passage.DeadTime == None
    ).first()
    
    if not passage:
        raise HTTPException(status_code=404, detail="通道未找到")
    
    # 检查区域是否存在
    if area_id:
        area = db.query(Area).filter(Area.AreaId == area_id).first()
        if not area:
            raise HTTPException(status_code=400, detail="区域不存在")
    
    # 更新区域
    passage.AreaId = area_id
    passage.UpdateTime = datetime.now()
    db.commit()
    
    return {"success": True, "message": "区域更新成功"}

@router.post("/video-channels/{passage_id}/edit")
async def edit_video_channel(
    passage_id: int,
    form_data: PassageForm,
    db: Session = Depends(get_db)
):
    """编辑视频通道"""
    try:
        # 获取现有通道 
        passage = db.query(Passage).filter(
            Passage.PassageId == passage_id,
            Passage.DeadTime == None
        ).first()
        
        if not passage:
            raise HTTPException(status_code=404, detail="通道未找到")
        
        # 检查通道名称是否被修改（不允许修改名称）
        if form_data.MediaName != passage.MediaName:
            raise HTTPException(status_code=400, detail="通道名称不可修改")
        
        # 获取设备IP地址
        device = db.query(Device).filter(Device.BoardId == form_data.BoardId).first()
        if not device:
            logger.warning(f"设备未找到: {form_data.BoardId}")
            raise HTTPException(status_code=400, detail="设备未找到")
        
        # 生成RTSP URL
        media_url = f"rtsp://{form_data.MediaUser}:{form_data.MediaPassword}@{form_data.MediaIp}:{form_data.MediaPort}/Streaming/Channels/{form_data.MediaNum}01"
        
        # 发送MQTT命令到设备
        mqtt_result = send_mqtt_command(
            ADD_EVENT,
            str(passage.PassageId), 
            media_url,
            form_data.BoardId
        )
        
        if not mqtt_result["success"]:
            raise HTTPException(status_code=400, detail=f"设备配置失败: {mqtt_result['message']}")
        
        # 更新通道信息
        passage.MediaDesc = form_data.MediaDesc
        passage.MediaIp = form_data.MediaIp
        passage.MediaPort = form_data.MediaPort
        passage.MediaUser = form_data.MediaUser
        passage.MediaPassword = form_data.MediaPassword
        passage.MediaNum = form_data.MediaNum
        passage.BoardId = form_data.BoardId
        passage.BoardIp = device.BoardIp  # 确保这里更新设备IP
        passage.AreaId = form_data.AreaId  # 更新区域ID
        passage.UpdateTime = datetime.now()
        
        # 提交事务（FastAPI会自动提交）
        db.commit()
        
        return {"success": True, "message": "通道更新成功"}
    
    except Exception as e:
        logger.exception(f"编辑通道时发生错误: {str(e)}")
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(status_code=500, detail="服务器内部错误")

@router.get("/video-channels/{passage_id}/delete")
async def delete_video_channel(
    passage_id: int,
    db: Session = Depends(get_db)
):
    """删除视频通道（软删除）"""
    logger.info(f"开始删除通道 ID: {passage_id}")
    
    try:
        # 只查询必要的字段，避免查询不存在的字段
        passage = db.query(
            Passage.PassageId,
            Passage.MediaName,
            Passage.BoardId
        ).filter(
            Passage.PassageId == passage_id,
            Passage.DeadTime == None
        ).first()
        
        if not passage:
            logger.warning(f"未找到通道 ID: {passage_id}")
            # 重定向到通道列表页，并显示错误消息
            return RedirectResponse(
                url=f"/video-channels?error=通道未找到",
                status_code=303
            )
        
        # 发送MQTT命令到设备
        mqtt_result = send_mqtt_command(
            DELETE_EVENT,
            str(passage.PassageId),
            "",
            passage.BoardId
        )
        
        if not mqtt_result["success"]:
            logger.error(f"设备删除命令失败: {mqtt_result['message']}")
            # 回滚事务（通过抛出异常让FastAPI自动回滚）
            raise HTTPException(status_code=400, detail=f"设备配置失败: {mqtt_result['message']}")
        
        # 使用update语句设置软删除标记，避免加载完整对象
        db.query(Passage).filter(
            Passage.PassageId == passage_id
        ).update({"DeadTime": datetime.now()})
        
        # 提交事务（FastAPI会自动提交）
        db.commit()
        logger.info(f"通道删除成功 ID: {passage_id}, 名称: {passage.MediaName}")
        
        # 重定向到通道列表页，并显示成功消息
        return RedirectResponse(
            url="/video-channels?success=通道已成功删除",
            status_code=303
        )
    
    except Exception as e:
        # 发生异常时回滚事务（FastAPI会自动回滚）
        logger.error(f"删除通道时发生错误: {str(e)}")
        # 重定向到通道列表页，并显示错误消息
        return RedirectResponse(
            url=f"/video-channels?error=删除通道时发生错误: {str(e)}",
            status_code=303
        )    